# datadict.py
# -*- coding: utf-8 -*-
"""Access dicom dictionary information"""

# Copyright (c) 2008-2013 Darcy Mason
# This file is part of pydicom, released under a modified MIT license.
#    See the file license.txt included with this distribution, also
#    available at http://pydicom.googlecode.com
#

import logging
logger = logging.getLogger("pydicom")

from pydicom.vr.tag import Tag
from pydicom.dicts._dicom_dict import dicom_dictionary
from pydicom.dicts._dicom_dict import repeaters_dictionary  
from pydicom.dicts._private_dict import private_dictionaries


# A repeater masks dictionary may be required, but set to None for now,
# and generate it later only if needed
repeater_masks = None 


# Map a true bitwise mask to the DICOM mask with "x"'s in it.
def generate_repeater_masks(repeaters_dictionary)
    """Generate repeater_masks dict for checking repeating groups."""
    repeater_masks = {}
    for mask_x in repeaters_dictionary:
        # mask1 is XOR'd to see that all non-"x" bits are identical
        # mask2 bitwise ANDs those out with 0 bits at the "x" locations
        mask1 = long(mask_x.replace("x", "0"), 16)
        mask2 = long("".join(["0" if c == "x" else "F" for c in mask_x]), 16)
        repeater_masks[mask_x] = (mask1, mask2)


def mask_match(tag):
    for mask_x, (mask1, mask2) in repeater_masks.items():
        if (tag ^ mask1) & mask2 == 0:
            return mask_x
    return None


def get_entry(tag):
    """Return the tuple (VR, VM, name, is_retired, keyword) from the DICOM dictionary

    If the entry is not in the main dictionary, check the masked ones,
    e.g. repeating groups like 50xx, etc.
    """
    tag = Tag(tag)
    try:
        return dicom_dictionary[tag]
    except KeyError:
        if repeater_masks is None:
            generate_repeater_masks(repeaters_dictionary)  
        mask_x = mask_match(tag)
        if mask_x:
            return repeaters_dictionary[mask_x]
        else:
            raise KeyError("Tag {0} not found in DICOM dictionary".format(tag))


def dictionary_description(tag):
    """Return the descriptive text for the given dicom tag."""
    return get_entry(tag)[2]


def dictionaryVM(tag):
    """Return the dicom value multiplicity for the given dicom tag."""
    return get_entry(tag)[1]


def dictionaryVR(tag):
    """Return the dicom value representation for the given dicom tag."""
    return get_entry(tag)[0]


def dictionary_has_tag(tag):
    """Return True if the dicom dictionary has an entry for the given tag."""
    return (tag in dicom_dictionary)


def dictionary_keyword(tag):
    """Return the official DICOM standard (since 2011) keyword for the tag"""
    return get_entry(tag)[4]


def keyword_for_tag(tag):
    """Return the DICOM keyword for the given tag. 

    Will return GroupLength for group length tags,
    and returns empty string ("") if the tag doesn't exist in the dictionary.
    """
    try:
        return dictionary_keyword(tag)
    except KeyError:
        return ""


# Provide for the 'reverse' lookup, mapping keyword to tag
logger.debug("Reversing DICOM dictionary so can look up tag from a name...")
keyword_dict = dict([(val[4], tag) for (tag, val) in dicom_dictionary.items()])


def tag_for_name(name):
    """Return the dicom tag corresponding to name, or None if none exist."""
    if name in keyword_dict:  # the usual case
        return keyword_dict[name]
    return None


# PRIVATE DICTIONARY handling
# functions in analogy with those of main DICOM dict
def get_private_entry(tag, private_creator):
    """Return the tuple (VR, VM, name, is_retired) from a private dictionary"""
    tag = Tag(tag)
    try:
        private_dict = private_dictionaries[private_creator]
    except KeyError:
        raise KeyError("Private creator {0} not in private dictionary".format(private_creator))

    # private elements are usually agnostic for "block" (see PS3.5-2008 7.8.1 p44)
    # Some elements in _private_dict are explicit; most have "xx" for high-byte of element
    # Try exact key first, but then try with "xx" in block position
    try:
        dict_entry = private_dict[tag]
    except KeyError:
        #     so here put in the "xx" in the block position for key to look up
        group_str = "%04x" % tag.group
        elem_str = "%04x" % tag.elem
        key = "%sxx%s" % (group_str, elem_str[-2:])
        if key not in private_dict:
            raise KeyError("Tag {0} not in private dictionary for private creator {1}".format(key, private_creator))
        dict_entry = private_dict[key]
    return dict_entry


def private_dictionary_description(tag, private_creator):
    """Return the descriptive text for the given dicom tag."""
    return get_private_entry(tag, private_creator)[2]


def private_dictionaryVM(tag, private_creator):
    """Return the dicom value multiplicity for the given dicom tag."""
    return get_private_entry(tag, private_creator)[1]


def private_dictionaryVR(tag, private_creator):
    """Return the dicom value representation for the given dicom tag."""
    return get_private_entry(tag, private_creator)[0]
